Skip to content

fix: show protocol handshake failures on the page (#213) - #260

Merged
schloerke merged 3 commits into
mainfrom
schloerke/fix-issue-213
Aug 28, 2026
Merged

fix: show protocol handshake failures on the page (#213)#260
schloerke merged 3 commits into
mainfrom
schloerke/fix-issue-213

Conversation

@schloerke

Copy link
Copy Markdown
Collaborator

Closes #213.

The protocol handshake throws during the first hook mount, so a mismatch left the page blank with the explanation only in the DevTools console.

What changed

Every fatal handshake path now goes through throwVisibly() in pkg-js/src/shiny-react/config.ts, which paints a fixed banner and then throws the same message:

  • major-version mismatch, either direction (assertProtocolCompatible)
  • npm build: missing #shinyreact-config tag
  • npm build: tag with no protocolVersion

Fail fast is unchanged — the banner is additive.

Details:

  • <div id="shinyreact-fatal-error" role="alert"> appended to <body>, one element reused across repeated failures.
  • Plain DOM, no dependency on Shiny being initialized: the failure being reported is precisely that client and server cannot talk to each other.
  • Literals (both versions, #shinyreact-config, @posit/shinyreact) are backticked in the message and render as <code> chips. Backticks are stripped from the thrown error and from the console-only warning.
  • Written with textContent, never innerHTML, so a server-supplied version string cannot inject markup.
  • #7f1d1d on #fee2e2 (~9.5:1) with a #991b1b bottom rule; chips ~11:1 on white. Never color alone — the full sentence and role="alert" carry the meaning.

What it looks like

shinyreact protocol mismatch: the server speaks protocol `999.0` but this JS
client supports `1.0`. Upgrade the older side (the shinyreact R/Python package,
or the client bundle) so the major protocol versions match.

(versions rendered as <code> chips, dark maroon on a pale red banner across the top)

Tests

  • pkg-js/src/shiny-react/__tests__/config.test.ts: both mismatch directions, chip markup, HTML-injection safety, single-element reuse, throw/display parity, backtick stripping. npx vitest run → 263 pass.
  • pkg-py/tests/playwright/test_protocol_mismatch.py + fixture apps/protocol_mismatch/ (server monkeypatched to claim protocol 999.0): banner visible with both versions, chips, role="alert", and the app body never rendered. make py-test-e2e → 11 pass.
  • make py-check-tests → 146 pass. make js-lint clean. make update-dist run.

FEATURES.md updated with the new [js] subtree.

Not done

No Shiny.notifications.show() / dev-mode error console surface. Shiny's error console is fed by server errors, not client throws, and notifications need Shiny initialized — exactly what is unreliable at the moment the handshake fails. Worth revisiting once the npm tier lands.

The handshake throws during the first hook mount, so a mismatch left a
blank page whose only explanation was a DevTools console message.

Every fatal handshake path now routes through `throwVisibly()`, which
paints a fixed `#shinyreact-fatal-error` banner (role="alert") carrying
the same text before throwing: the major mismatch in either direction,
and the npm build's missing-tag / missing-protocolVersion errors. Plain
DOM, no dependency on Shiny being initialized — the reported failure is
that client and server cannot talk to each other.

Literals (both versions, `#shinyreact-config`, `@posit/shinyreact`) are
marked with backticks and render as `<code>` chips; the backticks are
stripped from the thrown error and the console-only warning. Text is set
with textContent, so a server-supplied version cannot inject markup.
Colors are #7f1d1d on #fee2e2 (~9.5:1).

Tests: unit coverage in config.test.ts for both mismatch directions,
chip markup, HTML-injection safety, banner reuse, and throw/display
parity; a Playwright e2e (apps/protocol_mismatch) asserting the banner
is on screen and the app body never rendered.
# Conflicts:
#	pkg-js/dist/shinyreact.js
#	pkg-py/src/shinyreact/www/shinyreact.js
#	pkg-r/inst/lib/shiny/shinyreact.js
@schloerke
schloerke merged commit 2f63dce into main Aug 28, 2026
16 checks passed
@schloerke
schloerke deleted the schloerke/fix-issue-213 branch August 28, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Protocol handshake failure should be visible on the page, not only in DevTools

1 participant